<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Task;

use App\Model\Play;
use App\Model\Project;
use App\Utils\Enum\MatchEnum;
use App\Utils\Enum\RedbillEnum;

class UpdateRedbillProject extends AbstractBaseTask
{
    public function execute(): void
    {
        $this->updateSaleStatus();
        $this->settlement();
    }

    private function settlement(): void
    {
        $projectList = Project::query()->where('status', RedbillEnum::STATUS_SALE_OFF)->get()->toArray();
        if (empty($projectList)) {
            //无待结算方案
            return;
        }
        $map = [];
        foreach ($projectList as $i => $p) {
            $map[$p['match_id']][] = $i;
        }
        $matchIds = array_keys($map);

        //状态异常的比赛
        $abnormalMatchIds = Play::query()->whereIn('match_id', $matchIds)->where('match_status', '>', MatchEnum::MATCH_STATUS_END)->select('match_id')->get()->pluck('match_id')->toArray();
        //迟迟未开赛的比赛  比赛时间已过去1天，仍为待开赛状态的比赛
        $unknownErrorMatchIds = Play::query()->whereIn('match_id', $matchIds)->where([['match_status', '=', MatchEnum::MATCH_STATUS_WAIT], ['match_time', '<', time() - 86400]])->select('match_id')->get()->pluck('match_id')->toArray();
        if (!empty($abnormalMatchIds) || !empty($unknownErrorMatchIds)) {
            $total = Project::query()->whereIn('match_id', array_merge($abnormalMatchIds, $unknownErrorMatchIds))->update([
                'status' => RedbillEnum::STATUS_RESULT_PUBLISH,
                'hit_status' => 3, //走
            ]);
            $this->logger->warning(sprintf('%d个项目因比赛状态异常而置为走单', $total));
        }
        $finnishMatchList = Play::with(['data' => function ($q) {
            $q->select(['odds', 'match_id']);
        }])->whereIn('match_id', $matchIds)->where('match_status', MatchEnum::MATCH_STATUS_END)->get()->toArray();
        if (empty($finnishMatchList)) {
            //涉及比赛尚未结束
            return;
        }
        foreach ($finnishMatchList as $match) {
            $realMatchCodes = $this->getMatchResultCode($match);
            foreach ($map[$match['match_id']] as $i) {
                $project = &$projectList[$i];
                $suggestCodes = new_json_decode($project['suggest_result_code']);
                if (!isset($realMatchCodes[$project['play_type_code']])) {
                    Project::query()->where('project_id', $project['project_id'])->update([
                        'status' => RedbillEnum::STATUS_RESULT_PUBLISH,
                        'hit_status' => 3,
                        'real_result_code' => new_json_encode($realMatchCodes),
                    ]);
                    $this->logger->warning(sprintf('项目%s 的玩法 %d 不支持', $project['project_id'], $project['play_type_code']));
                    continue;
                }
                if ($realMatchCodes[$project['play_type_code']] == 'bs_zou') {
                    $hitStatus = 3;
                } else if (in_array($realMatchCodes[$project['play_type_code']], $suggestCodes)) {
                    $hitStatus = 1;
                } else {
                    $hitStatus = 2;
                }
                Project::query()->where('project_id', $project['project_id'])->update([
                    'status' => RedbillEnum::STATUS_RESULT_PUBLISH,
                    'hit_status' => $hitStatus,
                    'real_result_code' => new_json_encode($realMatchCodes),
                ]);
                //TODO 结算胜率，连红，回报率
            }
        }
    }

    /**
     * 计算比赛最终
     * @param $match
     * @return array
     */
    private function getMatchResultCode($match): array
    {
        $results = [];
        $scores = $this->calcMatchScores($match);
        $totalScore = array_sum($scores);
        $odds = $this->getOdds($match['data']['odds']);
        if ($match['sport_id'] == MatchEnum::SPORT_FOOTBALL) {
            //胜平负
            if ($scores[0] > $scores[1]) {
                $WDL = RedbillEnum::SUGGEST_FB_WDL_HOME_WIN;
            } elseif ($scores[0] == $scores[1]) {
                $WDL = RedbillEnum::SUGGEST_FB_WDL_DRAW;
            } else {
                $WDL = RedbillEnum::SUGGEST_FB_WDL_AWAY_WIN;
            }
            $results[RedbillEnum::PLAY_TYPE_FOOTBALL_WDL] = $WDL;

            //让球胜负
            if ($scores[0] - $scores[1] + floatval(isset($odds['asia']) ? $odds['asia'][1] : 0) > 0) {
                $val = RedbillEnum::SUGGEST_FB_BWL_HOME_WIN;
            } else {
                $val = RedbillEnum::SUGGEST_FB_BWL_AWAY_WIN;
            }
            $results[RedbillEnum::PLAY_TYPE_FOOTBALL_BWL] = $val;

            //大小球
            $_odds = $this->formatRq(isset($odds['bs']) ? $odds['bs'][1] : 0);
            if (count($_odds) == 1) {
                if ($totalScore > $_odds[0]) {
                    $val = RedbillEnum::SUGGEST_FB_BS_BIG;
                } elseif ($totalScore == $_odds[0]) {
                    $val = 'bs_zou';
                } else {
                    $val = RedbillEnum::SUGGEST_FB_BS_SMALL;
                }
            } else {
                if (in_array($totalScore, $_odds)) {
                    $val = 'bs_zou';
                } elseif ($totalScore > max($_odds)) {
                    $val = RedbillEnum::SUGGEST_FB_BS_BIG;
                } elseif ($totalScore < min($_odds)) {
                    $val = RedbillEnum::SUGGEST_FB_BS_SMALL;
                }
            }
            $results[RedbillEnum::PLAY_TYPE_FOOTBALL_BS] = $val;
        } else if ($match['sport_id'] == MatchEnum::SPORT_BASKETBALL) {
            //胜负
            if ($scores[0] > $scores[1]) {
                $val = RedbillEnum::SUGGEST_BB_WL_HOME_WIN;
            } else {
                $val = RedbillEnum::SUGGEST_BB_WL_AWAY_WIN;
            }
            $results[RedbillEnum::PLAY_TYPE_BASKETBALL_WL] = $val;

            //让分胜负
            if ($scores[0] - $scores[1] + floatval(isset($odds['asia']) ? $odds['asia'][1] : 0)) {
                $val = RedbillEnum::SUGGEST_BB_SWL_HOME_WIN;
            } else {
                $val = RedbillEnum::SUGGEST_BB_SWL_AWAY_WIN;
            }
            $results[RedbillEnum::PLAY_TYPE_BASKETBALL_SWL] = $val;

            //大小分
            if ($totalScore > floatval(isset($odds['asia']) ? $odds['asia'][1] : 0)) {
                $val = RedbillEnum::SUGGEST_BB_BS_BIG;
            } else {
                $val = RedbillEnum::SUGGEST_BB_BS_SMALL;
            }
            $results[RedbillEnum::PLAY_TYPE_BASKETBALL_BS] = $val;
        }
        return $results;
    }

    private function formatRq($odds): array
    {
        $odds = round(floatval($odds), 2);
        if ($odds) {
            $v = fmod($odds, 0.5);
            if ($v) {
                return [abs($odds - $v), abs($odds + $v)];
            } else {
                return [abs($odds)];
            }
        } else {
            return [abs($odds)];
        }
    }

    /**
     * 获取赔率
     * @param $odds
     * @param $companyId
     * @return array
     */
    private function getOdds($odds, $companyId = 2): array
    {
        if (!is_array($odds)) {
            $odds = new_json_decode($odds);
        }
        $data = [];
        foreach ($odds as $type => $rs) {
            if (!in_array($type, ['asia', 'bs', 'eu']) || empty($rs)) {
                continue;
            }
            foreach ($rs as $r) {
                if ($r['company']['id'] == $companyId && !empty($r['f']['odd'])) {
                    $data[$type] = array_slice($r['f']['odd'], 0, 3);
                }
            }
        }
        return $data;
    }

    /**
     * 计算比分
     * @param $match
     * @return array|int[]
     */
    private function calcMatchScores($match): array
    {
        $homeScores = new_json_decode($match['home_team_scores']);
        $awayScores = new_json_decode($match['away_team_scores']);
        if ($match['sport_id'] == MatchEnum::SPORT_FOOTBALL) {
            return [$homeScores[5] > 0 ? $homeScores[5] : $homeScores[0], $awayScores[5] > 0 ? $awayScores[5] : $awayScores[0]];
        } else {
            return [array_sum($homeScores), array_sum($awayScores)];
        }
    }

    /**
     * 开赛停售方案
     * @return void
     */
    private function updateSaleStatus(): void
    {
        $saleMatchIds = Project::query()->where('status', RedbillEnum::STATUS_SALE_ING)->select('match_id')->distinct()->get()->pluck('match_id')->toArray();
        if (!empty($saleMatchIds)) {
            $endMatchIds = Play::query()->whereIn('match_id', $saleMatchIds)->where('match_time', '<=', time())->select('match_id')->pluck('match_id');
            if (!empty($endMatchIds)) {
                $total = Project::query()->whereIn('match_id', $endMatchIds)->update(['status' => RedbillEnum::STATUS_SALE_OFF]);
                $this->logger->info(sprintf('%d个爆料状态更新成功', $total));
            }
        }
    }
}
